/**
 * 
 */
package com.hu.ta.assignment6.application;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;

import com.hu.ta.assignment6.beans.EmailClass;
import com.hu.ta.assignment6.beans.EmailContent;
import com.hu.ta.assignment6.beans.SpamClassifier;
import com.hu.ta.assignment6.evaluation.ComparisonResults;
import com.hu.ta.assignment6.io.FileData;
import com.hu.ta.assignment6.io.FileEmail;
import com.hu.ta.assignment6.io.FileIO;
import com.hu.ta.assignment6.io.FileListFilter;
import com.hu.ta.assignment6.io.FileResult;
import com.hu.ta.assignment6.ui.UIBuilder;
import com.hu.ta.assignment6.util.ConfigurationUtils;

/**
 * Logic unit of the application
 * 
 * @author cage@g.com
 */

public class Controller {

	private UIBuilder ui;
	private FileIO connections;
	private ConfigurationUtils configuration;
	private SpamClassifier classifier;

	public Controller() {
		super();
		configuration = ConfigurationUtils.getInstance();
		ui = new UIBuilder(this);
	}

	public void startProgram() {
		loadConfiguration();
		ui.startUI();

		// Training
		if (configuration.IsLearning()) {
			learn(false, getSpamFiles(), getHamFiles());
		} else {
			classifySpam(false,
					new File(configuration.getInputFolder()).listFiles());
		}
		ui.update(null, "Finished!");
	}

	public File[] getSpamFiles() {
		FilenameFilter select = new FileListFilter("0", null, Integer.MAX_VALUE);
		return new File(configuration.getSpamFolder()).listFiles(select);
	}

	public File[] getHamFiles() {
		FilenameFilter select = new FileListFilter("0", null, Integer.MAX_VALUE);
		return new File(configuration.getHamFolder()).listFiles(select);
	}

	public void doEvaluation(int numParts) {
		loadConfiguration();
		ui.startUI();

		File allSpamFiles[] = getSpamFiles();
		File allHamFiles[] = getHamFiles();

		ComparisonResults allRes = new ComparisonResults(0, 0, 0, 0, 0);
		for (int part = 0; part < numParts; part++) {
			ComparisonResults res; // = new ComparisonResults(0, 0, 0);
			int numSpams = 0;
			int numHams = 0;
			List<File> trainSpamFiles = new ArrayList<File>();
			List<File> trainHamFiles = new ArrayList<File>();
			List<File> evalSpamFiles = new ArrayList<File>();
			List<File> evalHamFiles = new ArrayList<File>();

			// First for Spam
			for (File file : allSpamFiles) {
				if (numSpams >= ((float) allSpamFiles.length / numParts) * part
						&& numSpams < ((float) allSpamFiles.length / numParts)
								* (part + 1)) {
					evalSpamFiles.add(file);
				} else {
					trainSpamFiles.add(file);
				}
				numSpams++;
			}

			// ... then for Ham
			for (File file : allHamFiles) {
				if (numHams >= ((float) allHamFiles.length / numParts) * part
						&& numHams < ((float) allHamFiles.length / numParts)
								* (part + 1)) {
					evalHamFiles.add(file);
				} else {
					trainHamFiles.add(file);
				}
				numHams++;
			}

			learn(true,
					trainSpamFiles.toArray(new File[trainSpamFiles.size()]),
					trainHamFiles.toArray(new File[trainHamFiles.size()]));
			FileEmail classifiedSpam[] = classifySpam(true,
					evalSpamFiles.toArray(new File[evalSpamFiles.size()]));
			FileEmail classifiedHam[] = classifySpam(true,
					evalHamFiles.toArray(new File[evalHamFiles.size()]));

			int FP = 0;
			int TP = 0;
			int FN = 0;
			int TN = 0;
			for (FileEmail file : classifiedSpam) {
				if (file.getClassification().isSpam()) {
					TP++;
				} else {
					FN++;
				}
			}
			for (FileEmail file : classifiedHam) {
				if (file.getClassification().isSpam()) {
					FP++;
				} else {
					TN++;
				}
			}

			res = new ComparisonResults(numSpams + numHams, TP, FP, FN, TN);
			allRes.add(res);
			ui.update(null, "Emails overall: " + res.getNum() + "\nPrecision: "
					+ res.getPrecision() + "\nRecall: " + res.getRecall()
					+ "\nF-Measure: " + res.getFMeasure() + "\nAccuracy: "
					+ res.getAccuracy());
		}
		ui.update(null, "Emails overall: " + allRes.getNum() + "\nPrecision: "
				+ allRes.getPrecision() + "\nRecall: " + allRes.getRecall()
				+ "\nF-Measure: " + allRes.getFMeasure() + "\nAccuracy: "
				+ allRes.getAccuracy());

		ui.update(null, "Finished!");
	}

	private void learn(boolean bEvaluation, File spamFiles[], File hamFiles[]) {
		List<FileEmail> trainSpams = new ArrayList<FileEmail>();
		List<FileEmail> trainHams = new ArrayList<FileEmail>();

		SpamClassifier classifi = new SpamClassifier();

		for (File txt : spamFiles) {
			FileEmail f = new FileEmail(txt.getAbsolutePath());
			f.read();
			trainSpams.add(f);
		}
		for (File txt : hamFiles) {
			FileEmail f = new FileEmail(txt.getAbsolutePath());
			f.read();
			trainHams.add(f);
		}

		classifi.train(trainSpams, trainHams);

		if (!bEvaluation) {
			connections = new FileData(configuration.getTrainedDataFile());
			((FileData) connections).setClassifier(classifi);
			connections.write();
		} else {
			this.classifier = classifi;
		}
	}

	private FileEmail[] classifySpam(boolean bEvaluation, File files[]) {
		SpamClassifier classifi;
		if (!bEvaluation) {
			connections = new FileData(configuration.getTrainedDataFile());
			connections.read();
			classifi = ((FileData) connections).getClassifier();
		} else {
			classifi = this.classifier;
		}

		FileEmail[] outputFiles = new FileEmail[files.length];
		int i = 0;
		for (File txt : files) {
			connections = new FileEmail(txt.getAbsolutePath());
			connections.read();
			EmailContent content = ((FileEmail) connections).getContent();

			EmailClass classified = classifi.classify(content);

			File path = txt;
			connections = new FileEmail(path.getAbsolutePath());
			((FileEmail) connections).setClassification(classified);
			outputFiles[i++] = (FileEmail) connections;
		}

		connections = new FileResult(configuration.getResultFile());
		((FileResult) connections).setResultList(outputFiles);
		connections.write();

		return outputFiles;
	}

	private void loadConfiguration() {
		// Given task: set only the source directory/no config file --> nothing
		// to do
	}
}
